package org.shejko.util.splitunite;

import java.util.Collection;
import java.util.Iterator;

/**
 * A simple implementation of {@code ISplitUnite} interface.
 * 
 * <p>Examples (assuming that <b>;</b> (semicolon) is separator and <b>!</b> (exclamation) is escape char):
 * <table border="1">
 * <tr><th>#</th><th>string</th><th>collection of strings</th><th>notes</th></tr>
 * <tr><td>0</td><td>"abcdef"</td><td>{ "abcdef" }</td><td></td></tr>
 * <tr><td>1</td><td>"abc;def"</td><td>{ "abc", "def" }</td><td></td></tr>
 * <tr><td>2</td><td>"abc!;def"</td><td>{ "abc;def" }</td><td>separator is escaped</td></tr>
 * <tr><td>3</td><td>"abc!!;def"</td><td>{ "abc!", "def" }</td><td>escape char is escaped, so separator remains unescaped and then breaks</td></tr>
 * <tr><td>4</td><td>"abc;def!"</td><td>{ "abc", "def!" }</td><td>the last escape char in the last word is not to be escaped</td></tr>
 * <tr><td>4a</td><td>"abc;!def"</td><td>{ "abc", "!def" }</td><td></td></tr>
 * <tr><td>5</td><td>"abc!!!;def"</td><td>{ "abc!;def" }</td><td></td></tr>
 * <tr><td>5a</td><td>"abc!;!def"</td><td>{ "abc;!def" }</td><td></td></tr>
 * <tr><td>6</td><td>"abc!!!!;def"</td><td>{ "abc!!", "def"} </td><td></td></tr>
 * <tr><td>7</td><td>"abc!!!def"</td><td>{ "abc!!!def" }</td><td>escape chars alone are not to be escaped</td></tr>
 * <tr><td>8</td><td>""</td><td>{}</td><td>empty string corresponds to empty collection</td></tr>
 * <tr><td>9</td><td>";"</td><td>{ "" }</td><td>extra separator is added in order to distinguish string corresponding to empty collection and string corresponding to collection with single empty string</td></tr>
 * <tr><td>A</td><td>";;"</td><td>{ "", "" }</td><td></td></tr>
 * <tr><td>B</td><td>";;;"</td><td>{ "", "", "" }</td><td></td></tr>
 * <tr><td>C</td><td>"abc;"</td><td>{ "abc", "" }</td><td></td></tr>
 * <tr><td>D</td><td>";def"</td><td>{ "", "def" }</td><td></td></tr>
 * <tr><td>E</td><td>"abc;;def"</td><td>{ "abc", "", "def" }</td><td></td></tr>
 * <tr><td>F</td><td>";!;!;;!;!"</td><td>{ "", ";;", ";!" }</td><td></td></tr>
 * </table>
 * @author dmytros
 */
public class SimpleSplitUnite implements ISplitUnite
{
    private final char sep_, esc_;

    public SimpleSplitUnite(char sep, char esc)
    {
        if (sep == esc)
        {
            throw new IllegalArgumentException(String.format("sep == esc == \\u%04x (%c)", (int) sep, sep));
        }
        sep_ = sep;
        esc_ = esc;
    }

    @Override
    public void split(String str, Collection<String> col)
    {
        StringBuilder tmp = new StringBuilder();
        boolean allEmpty = true;
        int f = 0, n;
        for (; (n = str.indexOf(sep_, f)) >= 0; f = n + 1)
        {
            int m = middle(str, f, n);
            int d = n - m;
            int h = m + d / 2;
            String part = str.substring(f, h);
            if ((d & 1) == 0)
            {
                String item = appendAndResetBuffer(tmp, part);
                allEmpty &= item.isEmpty();
                col.add(item);
            }
            else
            {
                tmp.append(part);
                tmp.append(sep_);
            }
        }
        {
            String item = appendAndResetBuffer(tmp, str.substring(f));
            allEmpty &= item.isEmpty();
            if (!allEmpty)
            {
                col.add(item);
            }
        }
    }

    String appendAndResetBuffer(StringBuilder tmp, String str)
    {
        if (tmp.length() > 0)
        {
            str = tmp.append(str).toString();
            tmp.setLength(0);
        }
        return str;
    }

    @Override
    public void unite(Iterable<String> col, StringBuilder builder)
    {
        Iterator<String> iter = col.iterator();
        boolean hasMore = iter.hasNext();
        boolean allEmpty = hasMore;
        while (hasMore)
        {
            String item = iter.next();
            hasMore = iter.hasNext();
            allEmpty &= appendForUnite(builder, item, hasMore);
        }
        if (allEmpty)
        {
            builder.append(sep_);
        }
    }

    boolean appendForUnite(StringBuilder builder, String item, boolean hasMore)
    {
        int f = 0, n;
        for (; (n = item.indexOf(sep_, f)) >= 0; f = n + 1)
        {
            int m = middle(item, f, n);
            // abc!!!;
            // ^  ^  ^
            // |  |  |
            // f  m  n
            builder.append(item.substring(f, n));
            builder.append(esc_);
            builder.append(item.substring(m, n + 1));
            // abc!!! ! !!!;
        }
        n = item.length();
        builder.append(item.substring(f));
        if (hasMore) {
            int m = middle(item, f, n);
            // abc!!!
            // ^  ^  ^
            // |  |  |
            // f  m  n
            builder.append(item.substring(m));
            // abc!!! !!!
            builder.append(sep_);
        }
        return item.isEmpty();
    }

    int middle(String item, int f, int n)
    {
        int m = n - 1;
        while (f <= m && item.charAt(m) == esc_) --m;
        return m + 1;
    }

}
